# Java 中的线程
单线程编程模型
Java 程序都运行在 JVM 中,程序中如果没有主动创建线程的话,只会创建一个线程,即主线程(不代表 JVM 中只有一个线程,JVM 在启动主线程时,也会启动一些其他线程,如 GC 线程)
用户线程和守护线程
线程启动前,可以使用 setDaemon(true)
设置为守护线程,默认用户线程
- 主要区别:是否阻止 JVM 正常退出(异常退出:例如
System.exit(int status)
退出 JVM (opens new window)) - 只要还有用户线程在运行,JVM 不会退出;如果没有用户线程,都是守护线程,JVM 结束
- 主线程只是一个普通的用户线程,且 main() 方法开始执行时,线程已经启动,不能再被设为守护线程
创建多线程主要有 3 个方法
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 Callable 接口
# Thread
Thread 是系统定义的一个线程处理类,任何子类只需要继承此类就可以获得线程处理的能力
Thread 继承了 Runnable 接口,子类继承 Thread 时,需要覆写 run() 方法,该方法将作为一个线程的「主方法」存在
代码样例
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
}
}
class Test {
public static void main(String[] args) {
MyThread threadA = new MyThread();
threadA.start(); // 继承自Thread的start()方法
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Runnable
Runnable 接口里同样存在 run() 方法,作为线程的主方法存在,但是缺少用于启动线程的 start() 方法
需要通过 public Thread(Runnable target)
间接启动线程
这种实现避免了继承所带来的单继承限制,同时 JDK1.8 之后,Runnable 成为了一个函数式接口,可以使用 Lambda 表达式简化代码
代码样例
@FunctionalInterface
public interface Runnable {
public void run();
}
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
}
}
class Test {
public static void main(String[] args) {
MyThread threadA = new MyThread();
new Thread(threadA).start();
// Or using Lambda
new Thread(() -> {
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
}).start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Callable
当前还存在一个问题,即 run() 方法没有返回值,从 JDK 1.5 开始提供的 Callable 接口可以解决
需要 FutureTask 和 Thread 协助启动线程并最终获得返回值,相关类关系如图
代码样例
@FunctionalInterface
public interface Callable<V> {
public V call() throws Exception;
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
return "Done";
}
}
class Test {
public static void main(String[] args) throws Exception {
Callable<String> callA = new MyThread("a");
FutureTask<String> futureA = new FutureTask<>(callA);
new Thread(futureA).start();
System.out.println("Results= " + futureA.get());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# run() 和 start() 方法
run()
:如果直接执行,则只是作为一个普通方法运行,无法创建新的线程start()
:创建新的线程,并将线程置于就绪态,等到获取 CPU 时间片,就开始执行 run() 方法
start() 方法里调用了 start0() 方法,该方法没有方法体,但是使用了 native 的关键字修饰,表示此操作将交由底层实现(JVM 实现,JVM 负责匹配不同 OS 的底层函数)
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21